home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Editing / XTLTest / xmltltst.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  38.8 KB  |  1,419 lines

  1. //------------------------------------------------------------------------------
  2. // File: XMLTlTst.cpp
  3. //
  4. // Desc: DirectShow sample code - test utility for building timelines from
  5. //       .XTL files.
  6. //
  7. // Copyright (c) 1999-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10. //
  11. // This sample demonstrates the following:
  12. // 
  13. // - Using IXml2Dex to load timelines from and save to .XTL files 
  14. // - Adding Windows Media (ASF) support to Editing applications
  15. // - Using IAMErrorLog to display error messages
  16. // - Preview of DES timelines (IAMTimeLine, IRenderEngine)
  17. // - Traversing the timeline nodes
  18. //
  19.  
  20. #include <windows.h>
  21. #include <streams.h>
  22. #include <stdio.h>
  23. #include <atlbase.h>
  24. #include <qedit.h>
  25.  
  26. extern CComModule _Module;
  27. #include <atlimpl.cpp>
  28.  
  29. //
  30. // NOTE:
  31. //
  32. // In order to write ASF files using this program, you need to obtain
  33. // a Windows Media Format SDK Certificate (WMStub.lib), link to it, 
  34. // and define USE_WMF_CERT below. 
  35. //
  36. // See the Windows Media Format SDK documentation for more information.
  37. //
  38. // The SDK download page is located at 
  39. // http://msdn.microsoft.com/workshop/imedia/windowsmedia/sdk/wmsdk.asp, 
  40. // with links to the SDK itself and information for obtaining a certificate.
  41. //
  42. // #define USE_WMF_CERT
  43. // 
  44.  
  45. #ifdef USE_WMF_CERT
  46.  
  47. #include <wmsdk.h>
  48. #include <dshowasf.h>
  49.  
  50.  
  51. // Note: this object is a SEMI-COM object, and can only be created statically.
  52. class CKeyProvider : public IServiceProvider {
  53. public:
  54.     STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
  55.     STDMETHODIMP_(ULONG) AddRef() { return 2; }
  56.     STDMETHODIMP_(ULONG) Release() { return 1; }
  57.  
  58.     // IServiceProvider
  59.     STDMETHODIMP QueryService(REFIID siid, REFIID riid, void **ppv);
  60.     
  61. };
  62.  
  63. CKeyProvider g_prov; // note, must stay in scope while graph is alive
  64.  
  65. #endif // USE_WMF_CERT
  66.  
  67.  
  68. class CErrorReporter : public IAMErrorLog
  69. {
  70.     // IAMErrorLog
  71.     STDMETHODIMP LogError( long Severity, BSTR ErrorString, LONG ErrorCode, 
  72.                            HRESULT hresult, VARIANT * pExtraInfo );
  73.  
  74. public:
  75.  
  76.     // SEMI COM
  77.     STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
  78.     {
  79.         if (riid == IID_IAMErrorLog || riid == IID_IUnknown) {
  80.             *ppv = (void *) static_cast<IAMErrorLog *>(this);
  81.             return NOERROR;
  82.         }    
  83.         return E_NOINTERFACE;
  84.     }
  85.     STDMETHODIMP_(ULONG) AddRef() { return 2; }
  86.     STDMETHODIMP_(ULONG) Release() { return 1; }
  87. };
  88.  
  89.  
  90. //
  91. // Declarations for helper functions defined below
  92. //
  93. IPin * GetPin( IBaseFilter * pFilter, int PinNum, PIN_DIRECTION pd );
  94.  
  95. void TurnOffPreviewMode( IAMTimeline * pTimeline );
  96.  
  97. HRESULT GetFirstSourceOnTimeline(
  98.     IAMTimeline *pTimeline, GUID MajorType,
  99.     IAMTimelineGroup ** ppGroup, IAMTimelineSrc ** ppSource );
  100.     
  101. HRESULT GetSourceVideoType(WCHAR *wszFilename, AM_MEDIA_TYPE *pmt);
  102.  
  103. HRESULT GetDestinationASFFormat(
  104.     AM_MEDIA_TYPE **ppmt,
  105.     int iProfile );
  106.  
  107. #ifdef USE_WMF_CERT
  108. HRESULT MapProfileIdToProfile(
  109.     int iProfile, IWMProfile **ppProfile);
  110. #endif
  111.  
  112. void ListWMSDKProfiles();
  113. BOOL IsAsfExtension( WCHAR * Filename );
  114.  
  115. HRESULT ConnectOutputFile(
  116.     IRenderEngine * pEngine, WCHAR * Filename
  117. #ifdef USE_WMF_CERT
  118.     , int iProfile
  119. #endif
  120.     );
  121.  
  122.  
  123. //
  124. // Main program code
  125. //
  126. int __cdecl main(int argc, char *argv[])
  127. {
  128.     CErrorReporter pLog; // note, must stay in scope while graph is alive
  129.     HRESULT hr = 0;
  130.  
  131.     ++argv; // skip the app name
  132.     --argc;
  133.  
  134.     int Ret = 0;
  135.  
  136.     char **&ppArg = argv;
  137.  
  138.     BOOL fNoRender = FALSE;
  139.     BOOL fDynamic = FALSE;
  140.     BOOL fRecomp = FALSE;
  141.     BOOL fWriteIt = FALSE;
  142.     int nWriteArg = -1;
  143.     BOOL fWriteGrf = FALSE;
  144.     int nWriteGrfArg = -1;
  145.     int nInputFileArg = -1;
  146.     BOOL fWriteXtl = FALSE;
  147.     int nWriteXtlArg = -1;
  148.     double RenderStart = -1.0;
  149.     double RenderStop = -1.0;
  150.     BOOL fNoClock = FALSE;
  151. #ifdef USE_WMF_CERT
  152.     int iASFProfile = -1;
  153. #endif
  154.  
  155.     // find which switches we've set. Every time we process a switch,
  156.     // we pull it off the stack
  157.     //
  158.     for( int arg = 0 ; arg < argc ; arg++ )
  159.     {
  160.         if( !ppArg[arg] ) continue;
  161.  
  162.         // found the input .xtl file specified
  163.         //
  164.         if( ppArg[arg][0] != '/' )
  165.         {
  166.             nInputFileArg = arg;
  167.             continue;
  168.         }
  169.  
  170.         if (ppArg[arg][1] == 'N' || ppArg[arg][1] == 'n') {
  171.             fNoRender = TRUE;
  172.             continue;
  173.         }
  174.  
  175.         if (ppArg[arg][1] == 'D' || ppArg[arg][1] == 'd') {
  176.             fDynamic = TRUE;
  177.             continue;
  178.         }
  179.  
  180.         if (ppArg[arg][1] == 'C' || ppArg[arg][1] == 'c') {
  181.             fNoClock = TRUE;
  182.             continue;
  183.         }
  184.  
  185.         if (ppArg[arg][1] == 'R' || ppArg[arg][1] == 'r') {
  186.             fRecomp = TRUE;
  187.             continue;
  188.         }
  189.         if (ppArg[arg][1] == 'W' || ppArg[arg][1] == 'w') {
  190.             fWriteIt = TRUE;
  191.             nWriteArg = arg + 1;
  192.             arg++; // advance beyond the extra arg
  193.             continue;
  194.         }
  195.         if (ppArg[arg][1] == 'G' || ppArg[arg][1] == 'g') {
  196.             fWriteGrf = TRUE;
  197.             nWriteGrfArg = arg + 1;
  198.             arg++; // advance beyond the extra arg
  199.             continue;
  200.         }
  201.         if (ppArg[arg][1] == 'X' || ppArg[arg][1] == 'x') {
  202.             fWriteXtl = TRUE;
  203.             nWriteXtlArg = arg + 1;
  204.             arg++; // advance beyond the extra arg
  205.             continue;
  206.         }
  207.  
  208. #ifdef USE_WMF_CERT        
  209.         if (ppArg[arg][1] == 'P' || ppArg[arg][1] == 'p') {
  210.             arg++;
  211.             if (arg >= argc || (ppArg[arg][0] < '0' || ppArg[arg][0] > '9')) {
  212.                 ListWMSDKProfiles();
  213.                 return -1;
  214.             }
  215.  
  216.             iASFProfile = atoiA(ppArg[arg]);
  217.             printf("Using WMSDK profile #%d\r\n", iASFProfile);
  218.             continue;
  219.         }
  220. #endif // USE_WMF_CERT
  221.         
  222.         if( ppArg[arg][1] == '[' ) {
  223.             // need to pull doubles out of the string
  224.             //
  225.             char * p = &ppArg[arg][2];
  226.             for( unsigned long k = 0 ; k < strlen( p ) ; k++ )
  227.             {
  228.                 if( p[k] == '-' )
  229.                 {
  230.                     break;
  231.                 }
  232.             }
  233.             if( p[k] != '-' )
  234.             {
  235.                 printf( "forgot the '-' between doubles\r\n" );
  236.                 return -1;
  237.             }
  238.             for( unsigned long j = 0 ; j < strlen( p ) ; j++ )
  239.             {
  240.                 if( p[j] == ']' )
  241.                 {
  242.                     break;
  243.                 }
  244.             }
  245.             if( p[j] != ']' )
  246.             {
  247.                 printf( "forgot the ']' \r\n" );
  248.                 return -1;
  249.             }
  250.             
  251.             // set it to zero so atof will work
  252.             //
  253.             p[k] = 0;
  254.             p[j] = 0;
  255.  
  256.             // get the float
  257.             //
  258.             RenderStart = atof( p );
  259.  
  260.             // get the next float
  261.             //
  262.             p = &p[k+1];
  263.  
  264.             RenderStop = atof( p );
  265.             continue;
  266.         }
  267.  
  268.  
  269.         printf("unrecognized switch: %s\n\n", ppArg[arg]);
  270.         nInputFileArg = -1;
  271.         break;
  272.     }
  273.     
  274.     if( argc < 1 || nInputFileArg == -1 ) {
  275.         printf("Usage:     [various switches] input.xtl\r\n");
  276.         printf("           /N  - No preview, just connect it up\r\n");
  277.         printf("           /G output.grf - Output a GRF file\r\n");
  278.         printf("           /X output.xtl - Output an XTL file\r\n");
  279.         printf("           /R  - Connect the graph with smart recompression turned on\r\n");
  280.         printf("           /W <filename> - Render the clip to file. (It may take a while)\r\n" );
  281.         printf("           /D  - Dynamic connections on\r\n");
  282.         printf("           /[double-double] - set the render range start/stop times\r\n" );
  283.         printf("           /C  - No clock. Run as fast as you can\r\n" );
  284. #ifdef USE_WMF_CERT        
  285.         printf("           /P [number] - Choose an ASF compression profile\r\n" );
  286.         printf("           /P without a number - list available profiles\r\n" );
  287. #endif // USE_WMF_CERT
  288.         return -1;
  289.     }
  290.  
  291.     USES_CONVERSION;
  292.     WCHAR *wszFileInput = A2W(ppArg[nInputFileArg]);
  293.  
  294.     CComPtr< IRenderEngine > pRenderEngine;
  295.     CComPtr< IAMTimeline > pTimeline;
  296.     CComPtr< IXml2Dex > pXml;
  297.  
  298.  
  299.     do
  300.     {
  301.         hr = CoInitialize( NULL );
  302.  
  303.         hr = CoCreateInstance( 
  304.             __uuidof(AMTimeline), 
  305.             NULL, 
  306.             CLSCTX_INPROC_SERVER, 
  307.             __uuidof(IAMTimeline), 
  308.             (void**) &pTimeline
  309.             );
  310.  
  311.         if (FAILED(hr)) {
  312.             printf("Failed to create timeline, error = %x\r\n", hr);
  313.             Ret = -1;
  314.             break;
  315.         }
  316.  
  317.         CComQIPtr< IAMSetErrorLog, &IID_IAMSetErrorLog > pTimelineLog( pTimeline );
  318.         if( pTimelineLog )
  319.         {
  320.             pTimelineLog->put_ErrorLog( &pLog );
  321.         }
  322.  
  323.         hr = CoCreateInstance( 
  324.             __uuidof(Xml2Dex), 
  325.             NULL, 
  326.             CLSCTX_INPROC_SERVER, 
  327.             __uuidof(IXml2Dex), 
  328.             (void**) &pXml
  329.             );
  330.     
  331.         if (FAILED(hr)) {
  332.             printf("QEDIT not registered properly\r\n");
  333.             Ret = -1;
  334.             break;
  335.         }
  336.  
  337.         
  338.         hr = pXml->ReadXMLFile(pTimeline, wszFileInput);
  339.         printf("ReadXMLFile('%ls') returned %x\r\n", wszFileInput, hr);
  340.         if (FAILED(hr)) {
  341.             Ret = -1;
  342.             break;
  343.         }
  344.  
  345.  
  346.         // validate sources
  347.         //
  348.         HANDLE WaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  349.  
  350.         long Flags =
  351.             SFN_VALIDATEF_CHECK |
  352.             SFN_VALIDATEF_POPUP | 
  353.             // SFN_VALIDATEF_TELLME |
  354.             SFN_VALIDATEF_REPLACE |
  355.             SFN_VALIDATEF_USELOCAL |
  356.             0;
  357.         
  358.         hr = pTimeline->ValidateSourceNames( Flags, NULL, (LONG_PTR) WaitEvent );
  359.         if( hr == NOERROR )
  360.         {
  361.             WaitForSingleObject( WaitEvent, INFINITE );
  362.         }
  363.         CloseHandle( WaitEvent );
  364.  
  365.         // create a render engine
  366.         //
  367.         if( fRecomp )
  368.         {
  369.             hr = CoCreateInstance( 
  370.                 __uuidof(SmartRenderEngine),
  371.                 NULL,
  372.                 CLSCTX_INPROC_SERVER,
  373.                 __uuidof(IRenderEngine),
  374.                 (void**) &pRenderEngine );
  375.  
  376.             TurnOffPreviewMode( pTimeline );
  377.         
  378. #ifdef USE_WMF_CERT
  379.             // if we're doing smart recompression then the AMTimeline object will 
  380.             // need a way to get our key, so we SetSite on the AMTimeline object here.
  381.             // Note that the pRenderEngine gets the key a little later than this.
  382.             if( SUCCEEDED( hr ) )
  383.             {
  384.                 CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pTimeline );
  385.                 ASSERT( pOWS );
  386.                 if( pOWS )
  387.                 {            
  388.                     pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
  389.                 }                
  390.             }
  391.  
  392. #endif // USE_WMF_CERT
  393.  
  394.             SCompFmt0 scompFmt;
  395.             ZeroMemory(&scompFmt, sizeof(scompFmt));
  396.             // scompFmt.nFormatId initialized to 0;
  397.             
  398.             // use the compression type of the first input video in
  399.             // the project (note this can change the height/width of
  400.             // the group)
  401.  
  402.             CComPtr<IAMTimelineGroup> pGroup;
  403.             CComPtr<IAMTimelineSrc> pSource;
  404.             hr = GetFirstSourceOnTimeline( pTimeline, MEDIATYPE_Video, &pGroup, &pSource);
  405.  
  406.  
  407. #ifdef USE_WMF_CERT
  408.  
  409.             // smart recompression works differently for ASF than it
  410.             // does for AVI. ASF compression needs to be configured by
  411.             // profile id rather than compressor and format block. We
  412.             // cannot recover the profile used to author an ASF file,
  413.             // so the caller is required to provide us the right
  414.             // profile id. We ask the WMSDK for the media type
  415.             // corresponding to the profile.
  416.             
  417.             WCHAR *wszFileTmp = A2W(ppArg[nWriteArg]);
  418.             if( IsAsfExtension( wszFileTmp ) )
  419.             {
  420.                 AM_MEDIA_TYPE *pmt;
  421.                 hr = GetDestinationASFFormat(
  422.                     &pmt, 
  423.                     iASFProfile);
  424.                 if(SUCCEEDED(hr)) {
  425.                     CopyMediaType(&scompFmt.MediaType, pmt);
  426.                     DeleteMediaType(pmt);
  427.                 }
  428.             }
  429.             else
  430. #endif // USE_WMF_CERT
  431.             {
  432.                 CComBSTR Filename;
  433.                 if(SUCCEEDED(hr)) {
  434.                     hr = pSource->GetMediaName(&Filename);
  435.                 }
  436.                 if(SUCCEEDED(hr)) {
  437.                     hr = GetSourceVideoType(Filename, &scompFmt.MediaType);
  438.                 }
  439.             }
  440.             
  441.             if(SUCCEEDED(hr)) {
  442.                 hr = pGroup->SetSmartRecompressFormat( (long*) &scompFmt );
  443.                 FreeMediaType(scompFmt.MediaType);
  444.             }
  445.         }
  446.         else
  447.         {
  448.             hr = CoCreateInstance( 
  449.                 __uuidof(RenderEngine),
  450.                 NULL,
  451.                 CLSCTX_INPROC_SERVER,
  452.                 __uuidof(IRenderEngine),
  453.                 (void**) &pRenderEngine );
  454.  
  455.         }
  456.         if (pRenderEngine == NULL) {
  457.             printf("Failed to create render engine, error = %x\r\n", hr);
  458.             Ret = -1;
  459.             break;
  460.         }
  461.  
  462. #ifdef USE_WMF_CERT
  463.  
  464.         //
  465.         // give the RenderEngine a key to support ASF
  466.         //
  467.         {
  468.             CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pRenderEngine );
  469.             ASSERT( pOWS );
  470.             if( pOWS )
  471.             {        
  472.                 pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
  473.             }            
  474.         }
  475. #endif // USE_WMF_CERT
  476.  
  477.         long gRenderMedLocFlags = 0 |
  478.             //        SFN_VALIDATEF_POPUP | 
  479.             //        SFN_VALIDATEF_TELLME |
  480.             //        SFN_VALIDATEF_VERBOSE | 
  481.             //        SFN_VALIDATEF_USELOCAL |
  482.             0;
  483.         
  484.         pRenderEngine->SetSourceNameValidation( NULL, NULL, gRenderMedLocFlags );
  485.  
  486.         // set it's timeline
  487.         //
  488.         hr = pRenderEngine->SetTimelineObject( pTimeline );
  489.         if (FAILED(hr)) {
  490.             printf("Failed to set timeline object, error = %x\r\n", hr);
  491.             Ret = -1;
  492.             break;
  493.         }
  494.  
  495.         // set dynamicness
  496.         //
  497.         if (fDynamic) {
  498.             pRenderEngine->SetDynamicReconnectLevel(CONNECTF_DYNAMIC_SOURCES);
  499.             printf("DYNAMIC ON\r\n");
  500.         } else {
  501.             pRenderEngine->SetDynamicReconnectLevel(CONNECTF_DYNAMIC_NONE);
  502.             printf("DYNAMIC OFF\r\n");
  503.         }
  504.  
  505.         // set the renderrange, if there is one
  506.         //
  507.         if( RenderStart != -1.0 && RenderStop != -1.0 )
  508.         {
  509.             pRenderEngine->SetRenderRange2( RenderStart, RenderStop );
  510.         }
  511.  
  512.         
  513.        
  514.         // connect up first half
  515.         //
  516.         hr = pRenderEngine->ConnectFrontEnd( );
  517.  
  518.         if( FAILED( hr ) )
  519.         {
  520.             printf("ConnectFrontEnd returned bomb code of %x\r\n", hr);
  521.             Ret = -1;
  522.             break;
  523.         }
  524.  
  525.  
  526.  
  527.         // get this now, because other calls will need it
  528.         //
  529.         CComPtr< IGraphBuilder > pGraph;
  530.         hr = pRenderEngine->GetFilterGraph( &pGraph );
  531.  
  532.         if (FAILED(hr)) {
  533.             printf("Couldn't get graph!  hr = %x", hr);
  534.             break;
  535.         }
  536.  
  537.         // if we didn't want to write the file to disk, may as well render it
  538.         //
  539.         if( !fWriteIt )
  540.         {
  541.             hr = pRenderEngine->RenderOutputPins( );
  542.  
  543.  
  544.             if (FAILED(hr)) 
  545.             {
  546.                 printf("RenderOutputPins returned bomb code of %x\r\n", hr);
  547.                 Ret = -1;
  548.                 break;
  549.             }
  550.  
  551.             if( fNoClock )
  552.             {
  553.                 CComQIPtr< IMediaFilter, &IID_IMediaFilter > pMFGraph( pGraph );
  554.                 if( pMFGraph )
  555.                 {
  556.                     pMFGraph->SetSyncSource( NULL );
  557.                 }
  558.             }
  559.         }
  560.         else
  561.         {
  562.             TurnOffPreviewMode( pTimeline );
  563.             WCHAR *wszFileTmp = A2W(ppArg[nWriteArg]);
  564.  
  565.             hr = ConnectOutputFile(pRenderEngine, wszFileTmp
  566. #ifdef USE_WMF_CERT
  567.                                    , iASFProfile
  568. #endif
  569.                                    );
  570.                                    
  571.             printf( "ConnectOutputFile returned %x\r\n", hr );
  572.             if( FAILED( hr ) )
  573.             {
  574.                 fNoRender = TRUE;
  575.             }
  576.         }
  577.  
  578.         // write out GRF files
  579.         //
  580.         if( fWriteGrf )
  581.         {
  582.             WCHAR *wszFileTmp = A2W(ppArg[nWriteGrfArg]);
  583.             hr = pXml->WriteGrfFile(pGraph, wszFileTmp);
  584.             printf("WriteGrfFile('%ls') returned %x\r\n", wszFileTmp, hr);
  585.         }
  586.         if( fWriteXtl )
  587.         {
  588.             WCHAR *wszFileTmp = A2W(ppArg[nWriteXtlArg]);
  589.             hr = pXml->WriteXMLFile(pTimeline, wszFileTmp);
  590.             printf("WriteXtlFile('%ls') returned %x\r\n", wszFileTmp, hr);
  591.         }
  592.  
  593.         if (!fNoRender) {
  594.             CComQIPtr< IMediaEvent, &IID_IMediaEvent > pEvent( pGraph );
  595.             CComQIPtr< IMediaControl, &IID_IMediaControl > pControl( pGraph );
  596.             hr = pControl->Run( );
  597.             if (FAILED(hr)) {
  598.                 printf("Failed to run the graph, error = %x\r\n", hr);
  599.                 Ret = -1;
  600.                 break;
  601.             }
  602.  
  603.  
  604.             HANDLE hEvent;
  605.  
  606.             if( !pEvent ) {
  607.                 // unexpected error
  608.                 Ret = -1;
  609.             }
  610.  
  611.             hr = pEvent->GetEventHandle((OAEVENT *)&hEvent);
  612.             if(FAILED(hr)) {
  613.                 // unexpected error
  614.                 Ret = -1;
  615.             }
  616.  
  617.             // wait for completion and dispatch messages for any windows
  618.             // created on our thread.
  619.             for(;;)
  620.             {
  621.                 while(MsgWaitForMultipleObjects(
  622.                     1,
  623.                     &hEvent,
  624.                     FALSE,
  625.                     INFINITE,
  626.                     QS_ALLINPUT) != WAIT_OBJECT_0)
  627.                 {
  628.                     MSG Message;
  629.  
  630.                     while (PeekMessage(&Message, NULL, 0, 0, TRUE))
  631.                     {
  632.                         TranslateMessage(&Message);
  633.                         DispatchMessage(&Message);
  634.                     }
  635.                 }
  636.  
  637.                 // event signaled. see if we're done.
  638.  
  639.                 LONG levCode;
  640.                 LONG_PTR lp1, lp2;
  641.  
  642.                 if(pEvent->GetEvent(&levCode, &lp1, &lp2, 0) == S_OK)
  643.                 {
  644.                     EXECUTE_ASSERT(SUCCEEDED(
  645.                         pEvent->FreeEventParams(levCode, lp1, lp2)));
  646.                 
  647.                     if(levCode == EC_COMPLETE || levCode == EC_ERRORABORT ||
  648.                        levCode == EC_USERABORT)
  649.                     {
  650.                         break;
  651.                     }
  652.                 }
  653.             }
  654.             
  655.             hr = pControl->Stop( );
  656.  
  657.             
  658.         }
  659.     } while(0); 
  660.  
  661.     pRenderEngine.Release( );
  662.     pXml.Release( );
  663.     if (pTimeline)
  664.     {
  665.         hr = pTimeline->ClearAllGroups();
  666.         pTimeline.Release( );
  667.     }
  668.  
  669.     CoUninitialize( );
  670.  
  671.     
  672.     return Ret;
  673. }
  674.  
  675. //
  676. // helper function to traverse a timline and return the first source
  677. // 
  678.  
  679. HRESULT GetFirstSourceOnTimeline(
  680.     IAMTimeline *pTimeline, GUID MajorType,
  681.     IAMTimelineGroup ** ppGroup, IAMTimelineSrc ** ppSource )
  682. {
  683.     *ppGroup = 0;
  684.     *ppSource = 0;
  685.     
  686.     for( long g = 0 ;; g++ )
  687.     {
  688.         // locate group of right media type (audio / video)
  689.         CComPtr< IAMTimelineObj > pGroupObj;
  690.         HRESULT hr = pTimeline->GetGroup( &pGroupObj, g );
  691.         if(FAILED(hr)) {
  692.             break;
  693.         }
  694.         CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pGroup( pGroupObj );
  695.  
  696.         CMediaType GroupType;
  697.         pGroup->GetMediaType( &GroupType );
  698.         if( GroupType.majortype != MajorType ) {
  699.             continue;
  700.         }
  701.  
  702.         // found the right group, go through each track in it
  703.  
  704.         long TrackCount = 0;
  705.         long Layers = 0;
  706.         pTimeline->GetCountOfType( g, &TrackCount, &Layers, TIMELINE_MAJOR_TYPE_TRACK );
  707.     
  708.         if( TrackCount < 1 )
  709.         {
  710.             continue;
  711.         }
  712.  
  713.         CComQIPtr< IAMTimelineComp, &IID_IAMTimelineComp > pGroupComp( pGroupObj );
  714.  
  715.         for( int CurrentLayer = 0 ; CurrentLayer < Layers ; CurrentLayer++ )
  716.         {
  717.             // get the layer itself
  718.             //
  719.             CComPtr< IAMTimelineObj > pLayer;
  720.             hr = pGroupComp->GetRecursiveLayerOfType( &pLayer, CurrentLayer, TIMELINE_MAJOR_TYPE_TRACK );
  721.  
  722.             // ask if the layer is muted
  723.             //
  724.             BOOL LayerMuted = FALSE;
  725.             pLayer->GetMuted( &LayerMuted );
  726.             if( LayerMuted )
  727.             {
  728.                 // don't look at this layer
  729.                 //
  730.                 continue; // skip this layer, don't worry about grid
  731.             }
  732.  
  733.             CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack( pLayer );
  734.             if( !pTrack )
  735.             {
  736.                 continue;
  737.             }
  738.  
  739.             REFERENCE_TIME InOut = 0;
  740.             while( 1 )
  741.             {
  742.                 // get the next source on this layer, given a time.
  743.                 //
  744.                 CComPtr< IAMTimelineObj > pSourceObj;
  745.                 hr = pTrack->GetNextSrc( &pSourceObj, &InOut );
  746.                 ASSERT( !FAILED( hr ) );
  747.                 if( hr != NOERROR )
  748.                 {
  749.                     // all done with sources
  750.                     //
  751.                     break;
  752.                 }
  753.  
  754.                 CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource( pSourceObj );
  755.                 ASSERT( pSource );
  756.                 if( !pSource )
  757.                 {
  758.                     // this one failed, so look at the next
  759.                     //
  760.                     continue; // sources
  761.                 }
  762.  
  763.                 // ask if the source is muted
  764.                 //
  765.                 BOOL SourceMuted = FALSE;
  766.                 pSourceObj->GetMuted( &SourceMuted );
  767.                 if( SourceMuted )
  768.                 {
  769.                     // don't look at this source
  770.                     //
  771.                     continue; // sources
  772.                 }
  773.  
  774.                 *ppSource = pSource;
  775.                 pSource.p->AddRef();
  776.                 *ppGroup = pGroup;
  777.                 pGroup.p->AddRef();
  778.  
  779.                 return S_OK;
  780.  
  781.             } // while sources
  782.  
  783.  
  784.         } // while currentlayer
  785.  
  786.     } // for all groups
  787.  
  788.     return VFW_E_NOT_FOUND;
  789. }
  790.  
  791. void TurnOffPreviewMode( IAMTimeline * pTimeline )
  792. {
  793.     long Groups = 0;
  794.     pTimeline->GetGroupCount( &Groups );
  795.     for( int i = 0 ; i < Groups ; i++ )
  796.     {
  797.         CComPtr< IAMTimelineObj > pGroupObj;
  798.         pTimeline->GetGroup( &pGroupObj, i );
  799.         CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pGroup( pGroupObj );
  800.         pGroup->SetPreviewMode( FALSE );
  801.     }
  802. }
  803.  
  804. //
  805. // uses the media detector to retrieve mediatype of a clip
  806. // 
  807. HRESULT GetSourceVideoType(WCHAR *wszFilename, AM_MEDIA_TYPE *pmt)
  808. {
  809.     CComPtr< IMediaDet > pDet;
  810.     HRESULT hr = CoCreateInstance( CLSID_MediaDet,
  811.                            NULL,
  812.                            CLSCTX_INPROC_SERVER,
  813.                            IID_IMediaDet,
  814.                            (void**) &pDet );
  815.     if( FAILED( hr ) ) {
  816.         return hr;
  817.     }
  818.  
  819. #ifdef USE_WMF_CERT
  820.  
  821.     //
  822.     // set the site provider on the MediaDet object to allowed keyed apps
  823.     // to use ASF decoder
  824.     //
  825.     CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pDet );
  826.     if( pOWS )
  827.     {
  828.         pOWS->SetSite( (IServiceProvider *) &g_prov );
  829.     }
  830.  
  831. #endif // USE_WMF_CERT
  832.  
  833.     hr = pDet->put_Filename( wszFilename );
  834.     if( FAILED( hr ) )
  835.     {
  836.         return hr;
  837.     }
  838.  
  839.     // go through and find the video stream type
  840.     //
  841.     long Streams = 0;
  842.     long VideoStream = -1;
  843.     hr = pDet->get_OutputStreams( &Streams );
  844.     for( int i = 0 ; i < Streams ; i++ )
  845.     {
  846.         pDet->put_CurrentStream( i );
  847.         GUID Major = GUID_NULL;
  848.         pDet->get_StreamType( &Major );
  849.         if( Major == MEDIATYPE_Video )
  850.         {
  851.             VideoStream = i;
  852.             break;
  853.         }
  854.     }
  855.     if( VideoStream == -1 )
  856.     {
  857.         return VFW_E_INVALIDMEDIATYPE;
  858.     }
  859.  
  860.     hr = pDet->get_StreamMediaType( pmt );
  861.     return hr;
  862. }
  863.  
  864.  
  865. #ifdef USE_WMF_CERT
  866.  
  867. // ------------------------------------------------------------------------
  868. // CKeyProvider methods
  869. // 
  870.  
  871. HRESULT CKeyProvider::QueryInterface(REFIID riid, void ** ppv)
  872. {
  873.     if (riid == IID_IServiceProvider || riid == IID_IUnknown) {
  874.         *ppv = (void *) static_cast<IServiceProvider *>(this);
  875.         return NOERROR;
  876.     }    
  877.     return E_NOINTERFACE;
  878. }
  879.  
  880.  
  881. STDMETHODIMP CKeyProvider::QueryService(REFIID siid, REFIID riid, void **ppv)
  882. {
  883.     if (siid == __uuidof(IWMReader) && riid == IID_IUnknown) {
  884.     
  885.         IUnknown *punkCert;
  886.  
  887.         HRESULT hr = WMCreateCertificate( &punkCert );
  888.         if (SUCCEEDED(hr)) {
  889.             *ppv = (void *) punkCert;
  890.         }
  891.         return hr;
  892.     }
  893.     return E_NOINTERFACE;
  894. }
  895.  
  896. #endif // USE_WMF_CERT
  897.  
  898. // ------------------------------------------------------------------------
  899. // CErrorReporter methods
  900. // 
  901.  
  902. STDMETHODIMP CErrorReporter::LogError( long Severity, BSTR ErrorString, LONG ErrorCode, HRESULT hresult, VARIANT * pExtraInfo )
  903. {
  904.     USES_CONVERSION;
  905.     printf( "Error %d (HRESULT %x)\r\n", ErrorCode, hresult);
  906.     char * t = W2A( ErrorString );
  907.     printf( t );
  908.     printf( "\r\n" );
  909.  
  910. #ifdef DEBUG
  911.     OutputDebugStringA( t );
  912.     OutputDebugString( TEXT("\r\n") );
  913. #endif // DEBUG
  914.  
  915.     // look at variant
  916.     //
  917.     if( pExtraInfo )
  918.     {
  919.         if( pExtraInfo->vt == VT_BSTR )
  920.         {
  921.             printf( "Extra Info:%ls\r\n", pExtraInfo->bstrVal);
  922.         } else if( pExtraInfo->vt == VT_I4 ) {
  923.             printf( "Extra Info: %d\r\n", (int)pExtraInfo->lVal );
  924.         } else if( pExtraInfo->vt == VT_R8 ) {
  925.             printf( "Extra Info: %d/100\r\n",
  926.                     (int)(V_R8(pExtraInfo) * 100));
  927.         }
  928.     }
  929.     return hresult;
  930. }
  931.  
  932. #ifdef USE_WMF_CERT
  933.  
  934. // list formats Windows Media Format SDK supports.
  935. // 
  936. void ListWMSDKProfiles()
  937. {
  938.     USES_CONVERSION;
  939.     
  940.     int wextent = 0 ;
  941.     int Loop = 0 ;
  942.     SIZE extent ;
  943.     DWORD cProfiles = 0 ;
  944.  
  945.     printf("Standard system profiles:\n");
  946.     
  947.     CComPtr <IWMProfileManager> pIWMProfileManager;
  948.  
  949.     HRESULT hr = WMCreateProfileManager( &pIWMProfileManager );
  950.     if( FAILED( hr ) ) {   
  951.         return; // error
  952.     }        
  953.  
  954.     IWMProfileManager2* pIPM2;
  955.     hr = pIWMProfileManager->QueryInterface( IID_IWMProfileManager2,
  956.                                              ( void ** )&pIPM2 );
  957.     if(FAILED(hr)) {
  958.         return;
  959.     }
  960.     // we only use 7_0 profiles
  961.     hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 );
  962.     pIPM2->Release();
  963.     
  964.     if(FAILED(hr)) {
  965.         return;
  966.     }
  967.         
  968.     hr = pIWMProfileManager->GetSystemProfileCount(  &cProfiles );
  969.     if( FAILED( hr ) )
  970.     {
  971.         return;
  972.     }
  973.         
  974.     //    
  975.     // now load the profile strings
  976.     //    
  977.     LRESULT ix;
  978.     DWORD cchName, cchDescription;
  979.     for (int i = 0; i < (int)cProfiles; ++i) {
  980.         CComPtr <IWMProfile> pIWMProfile;
  981.         
  982.         hr = pIWMProfileManager->LoadSystemProfile( i, &pIWMProfile );
  983.         if( FAILED( hr ) )
  984.             return;
  985.             
  986.         hr = pIWMProfile->GetName( NULL, &cchName );
  987.         if( FAILED( hr ) )
  988.             return;
  989.             
  990.         WCHAR *wszProfile = new WCHAR[ cchName ];
  991.         if( NULL == wszProfile )
  992.             return;
  993.             
  994.         hr = pIWMProfile->GetName( wszProfile, &cchName );
  995.         if( FAILED( hr ) )
  996.             return;
  997.         
  998.         hr = pIWMProfile->GetDescription( NULL, &cchDescription );
  999.         if( FAILED( hr ) )
  1000.             return;
  1001.             
  1002.         WCHAR *wszDescription = new WCHAR[ cchDescription ];
  1003.         if( NULL == wszDescription )
  1004.             return;
  1005.             
  1006.         
  1007.         hr = pIWMProfile->GetDescription( wszDescription, &cchDescription );
  1008.         if( FAILED( hr ) )
  1009.             return;
  1010.         
  1011.  
  1012.         printf("  %3d:  %ls\n", i, wszProfile);
  1013.         
  1014.         delete[] wszProfile;
  1015.         delete[] wszDescription;
  1016.     }
  1017.  
  1018.     printf("\r\n Use /p [#] to choose the profile you want\r\n"); 
  1019. }
  1020. #endif // USE_WMF_CERT
  1021.  
  1022. // 
  1023. // helper function to return Nth input pin or output pin on a
  1024. // filter. pin is addref'd
  1025. // 
  1026. IPin * GetPin( IBaseFilter * pFilter, int PinNum, PIN_DIRECTION pd )
  1027. {
  1028.     CComPtr<IEnumPins> pEnum;
  1029.     HRESULT hr = pFilter->EnumPins( &pEnum );
  1030.     if(SUCCEEDED(hr))
  1031.     {
  1032.         ULONG cFetched;
  1033.         IPin * pPin;
  1034.         while(pEnum->Next( 1, &pPin, &cFetched) == S_OK)
  1035.         {
  1036.             PIN_DIRECTION pd2;
  1037.             pPin->QueryDirection( &pd2 );
  1038.             if( pd2 == pd )
  1039.             {
  1040.                 if( PinNum == 0 )
  1041.                 {
  1042.                     // return addref'd pin
  1043.                     return pPin;
  1044.                 }
  1045.                 PinNum--;
  1046.             }
  1047.  
  1048.             pPin->Release();
  1049.         }
  1050.     }
  1051.  
  1052.     return NULL;
  1053. }
  1054.  
  1055. //
  1056. // Configure the graph to write to an AVI file (or ASF or .WAV)
  1057. //
  1058.  
  1059. HRESULT ConnectOutputFile(
  1060.     IRenderEngine * pEngine, WCHAR * Filename
  1061. #ifdef USE_WMF_CERT
  1062.     , int iProfile
  1063. #endif
  1064.     )
  1065. {
  1066.     CComPtr< ICaptureGraphBuilder2 > pBuilder;
  1067.     HRESULT hr = CoCreateInstance(
  1068.         CLSID_CaptureGraphBuilder2,
  1069.         NULL,
  1070.         CLSCTX_INPROC_SERVER,
  1071.         IID_ICaptureGraphBuilder2,
  1072.         (void**) &pBuilder );
  1073.     if( FAILED( hr ) )
  1074.     {
  1075.         return hr;
  1076.     }
  1077.  
  1078.     CComPtr< IAMTimeline > pTimeline;
  1079.     hr = pEngine->GetTimelineObject( &pTimeline );
  1080.     if( FAILED( hr ) )
  1081.     {
  1082.         return hr;
  1083.     }
  1084.  
  1085.     CComPtr< IGraphBuilder > pGraph;
  1086.     hr = pEngine->GetFilterGraph( &pGraph );
  1087.     if( FAILED( hr ) )
  1088.     {
  1089.         return hr;
  1090.     }
  1091.  
  1092.     long Groups = 0;
  1093.     pTimeline->GetGroupCount( &Groups );
  1094.     if( !Groups )
  1095.     {
  1096.         return E_INVALIDARG;
  1097.     }
  1098.  
  1099.     CComPtr< IBaseFilter > pMux;
  1100.     CComPtr< IFileSinkFilter > pWriter;
  1101.  
  1102.     hr = pBuilder->SetFiltergraph( pGraph );
  1103.     if( FAILED( hr ) )
  1104.     {
  1105.         return hr;
  1106.     }
  1107.  
  1108.  
  1109.     bool fConnectManually = false;
  1110.     GUID guid = MEDIASUBTYPE_Avi;
  1111.  
  1112.     // determine which writer to use based on file extension
  1113.     if (lstrlenW(Filename) > 4)
  1114.     {
  1115.         WCHAR *pExt = Filename + lstrlenW(Filename) - 3;
  1116.  
  1117. #ifdef USE_WMF_CERT
  1118.         if(IsAsfExtension(pExt))
  1119.         {
  1120.             guid =  CLSID_WMAsfWriter;
  1121.         }
  1122. #endif // USE_WMF_CERT
  1123.         if (lstrcmpiW(pExt, L"wav") == 0)
  1124.         {
  1125.             fConnectManually = true;
  1126.             
  1127.             // creation of the wav writer filter SDK sample
  1128.             //
  1129.             // .wav files need to be special-cased here because the
  1130.             // capture graph builder doesn't know about the wav writer
  1131.             // filter.
  1132.             // 
  1133.             // {3C78B8E2-6C4D-11d1-ADE2-0000F8754B99}
  1134.             const CLSID CLSID_WavDest = {
  1135.                 0x3C78B8E2,0x6C4D,0x11D1,{0xAD,0xE2,0x00,0x00,0xF8,0x75,0x4B,0x99}
  1136.             };
  1137.  
  1138.             hr = CoCreateInstance(
  1139.                 CLSID_WavDest,
  1140.                 NULL,
  1141.                 CLSCTX_INPROC_SERVER,
  1142.                 IID_IBaseFilter,
  1143.                 (void**) &pMux );
  1144.             if( SUCCEEDED( hr ) )
  1145.             {
  1146.                 hr = pGraph->AddFilter( pMux, L"Wave Mux" );
  1147.             }
  1148.  
  1149.             if( SUCCEEDED( hr ) )
  1150.             {
  1151.                 hr = CoCreateInstance(
  1152.                     CLSID_FileWriter,
  1153.                     NULL,
  1154.                     CLSCTX_INPROC_SERVER,
  1155.                     IID_IFileSinkFilter,
  1156.                     (void**) &pWriter );
  1157.             }
  1158.             if( SUCCEEDED( hr ) )
  1159.             {
  1160.                 hr = pWriter->SetFileName( Filename, NULL );
  1161.             }
  1162.             if( SUCCEEDED( hr ) )
  1163.             {
  1164.                 CComQIPtr< IBaseFilter, &IID_IBaseFilter > pWriterBase( pWriter );
  1165.                 hr = pGraph->AddFilter( pWriterBase, L"Writer" );
  1166.             }
  1167.         }
  1168.     }
  1169.  
  1170.     if(FAILED(hr)) {
  1171.         return hr;
  1172.     }
  1173.  
  1174.     if(!fConnectManually)
  1175.     {
  1176.         hr = pBuilder->SetOutputFileName(
  1177.             &guid, Filename, &pMux, &pWriter );
  1178.     }
  1179.     if( FAILED( hr ) )
  1180.     {
  1181.         return hr;
  1182.     }
  1183.  
  1184.     CComQIPtr<IConfigInterleaving, &IID_IConfigInterleaving> pConfigInterleaving(pMux);
  1185.     if(pConfigInterleaving) {
  1186.         pConfigInterleaving->put_Mode(INTERLEAVE_FULL);
  1187.     }
  1188.     CComQIPtr<IFileSinkFilter2, &IID_IFileSinkFilter2> pCfgFw(pWriter);
  1189.     if(pCfgFw) {
  1190.         pCfgFw->SetMode(AM_FILE_OVERWRITE);
  1191.     }
  1192.      
  1193. #ifdef USE_WMF_CERT
  1194.     CComQIPtr<IConfigAsfWriter, &IID_IConfigAsfWriter> pConfigAsfWriter(pMux);
  1195.     if(pConfigAsfWriter && iProfile >= 0)
  1196.     {
  1197.         CComPtr<IWMProfile> pProfile;
  1198.         hr = MapProfileIdToProfile(iProfile, &pProfile);
  1199.         if(FAILED(hr)) {
  1200.             return hr;
  1201.         }
  1202.  
  1203.         // note that the ASF writer will not run if the number of streams
  1204.         // does not match the profile.
  1205.         hr = pConfigAsfWriter->ConfigureFilterUsingProfile(pProfile);
  1206.         if(FAILED(hr)) {
  1207.             return hr;
  1208.         }
  1209.     }
  1210. #endif
  1211.  
  1212.     for( int g = 0 ; g < Groups ; g++ )
  1213.     {
  1214.         CComPtr< IPin > pPin;
  1215.         hr = pEngine->GetGroupOutputPin( g, &pPin );
  1216.         if( FAILED( hr ) )
  1217.         {
  1218.             return hr;
  1219.         }
  1220.  
  1221.         // connect pin to the mux
  1222.         //
  1223.         hr = pBuilder->RenderStream( NULL, NULL, pPin, NULL, pMux );
  1224.         if( FAILED( hr ) )
  1225.         {
  1226.             return hr;
  1227.         }
  1228.     }
  1229.  
  1230.     if( fConnectManually )
  1231.     {
  1232.         // connect the mux to the writer. the wav mux refuses to
  1233.         // connect its output pin until its input pin is
  1234.         // connected. Otherwise this could be done earlier.
  1235.         //
  1236.         CComQIPtr< IBaseFilter, &IID_IBaseFilter > pWriterBase( pWriter );
  1237.         CComPtr<IPin> pMuxOut, pWriterIn;
  1238.         pMuxOut.p = GetPin( pMux, 0, PINDIR_OUTPUT );
  1239.         pWriterIn.p = GetPin( pWriterBase, 0, PINDIR_INPUT );
  1240.         if(!pMuxOut || !pWriterIn) {
  1241.             return E_UNEXPECTED;
  1242.         }
  1243.         hr = pGraph->Connect( pMuxOut, pWriterIn );
  1244.     }
  1245.  
  1246.     return hr;
  1247. }
  1248.  
  1249. BOOL IsAsfExtension( WCHAR *Filename )
  1250. {
  1251.     if (lstrlenW(Filename) >= 3)
  1252.     {
  1253.         WCHAR *pExt = Filename + lstrlenW(Filename) - 3;
  1254.  
  1255.         if(lstrcmpiW(pExt, L"asf") == 0 ||
  1256.            lstrcmpiW(pExt, L"wma") == 0 ||
  1257.            lstrcmpiW(pExt, L"wmv") == 0)
  1258.         {
  1259.             return TRUE;
  1260.         }
  1261.     }
  1262.     return FALSE;
  1263. }
  1264.  
  1265. //
  1266. // Get the mediatype for a particular ASF profile. it does this using
  1267. // the DirectShow components that use the WMSDK rather than through
  1268. // the WMSDK directly.
  1269. //
  1270.  
  1271. #ifdef USE_WMF_CERT
  1272.  
  1273. HRESULT GetDestinationASFFormat(
  1274.     AM_MEDIA_TYPE **ppmt,
  1275.     int iProfile
  1276.     )
  1277. {
  1278.     *ppmt = 0;
  1279.     
  1280.     CComPtr< IConfigAsfWriter > pWriter;
  1281.     HRESULT hr = CoCreateInstance(
  1282.         CLSID_WMAsfWriter,
  1283.         NULL,
  1284.         CLSCTX_INPROC_SERVER,
  1285.         IID_IConfigAsfWriter,
  1286.         (void **)&pWriter);
  1287.     if(FAILED(hr)) {
  1288.         return hr;
  1289.     }
  1290.  
  1291.     CComQIPtr<IBaseFilter,&IID_IBaseFilter> pwf(pWriter);
  1292.     if(!pWriter) {
  1293.         return E_UNEXPECTED;
  1294.     }
  1295.  
  1296.     // we have to add the ASF writer to a dummy graph in order for the
  1297.     // calls to work.
  1298.     CComPtr< IGraphBuilder > pBuilder;
  1299.     hr = CoCreateInstance(
  1300.         CLSID_FilterGraph,
  1301.         NULL,
  1302.         CLSCTX_INPROC_SERVER,
  1303.         IID_IGraphBuilder,
  1304.         (void**) &pBuilder );
  1305.     if( FAILED( hr ) )
  1306.     {
  1307.         return hr;
  1308.     }
  1309.  
  1310.     //
  1311.     // give the graph a key to support ASF
  1312.     //
  1313.     {
  1314.         CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pBuilder );
  1315.         ASSERT( pOWS );
  1316.         if( pOWS )
  1317.         {        
  1318.             pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
  1319.         }            
  1320.     }
  1321.  
  1322.     hr = pBuilder->AddFilter(pwf, 0);
  1323.     if(FAILED(hr)) {
  1324.         return hr;
  1325.     }
  1326.  
  1327.     // we use profile id -1 to indicate no profile was set.
  1328.     if (iProfile >= 0)
  1329.     {
  1330.         CComPtr<IWMProfile> pProfile;
  1331.         hr = MapProfileIdToProfile(iProfile, &pProfile);
  1332.         if(FAILED(hr)) {
  1333.             return hr;
  1334.         }
  1335.         hr = pWriter->ConfigureFilterUsingProfile(pProfile);
  1336.         if(FAILED(hr)) {
  1337.             return hr;
  1338.         }
  1339.     }
  1340.  
  1341.     CComPtr<IEnumPins> pEnumPins;
  1342.     hr = pwf->EnumPins( &pEnumPins );
  1343.     if(FAILED(hr)) {
  1344.         return hr;
  1345.     }
  1346.     
  1347.     // get destination video format from ASF writer using IAMStreamConfig::GetFormat on
  1348.     // its input pins
  1349.     int iPins = 0;
  1350.     ULONG ul = 0;
  1351.     CComPtr < IPin > pWriterInputPin; // we only expect to find input pins
  1352.     while( S_OK == pEnumPins->Next( 1, &pWriterInputPin, &ul ) )
  1353.     {
  1354. #ifdef DEBUG
  1355.         {
  1356.             PIN_DIRECTION pd;
  1357.             pWriterInputPin->QueryDirection( &pd );
  1358.             ASSERT( PINDIR_OUTPUT != pd );
  1359.         }
  1360. #endif
  1361.         CComQIPtr <IAMStreamConfig, &IID_IAMStreamConfig> pStreamConfig( pWriterInputPin);
  1362.         if( !pStreamConfig ) {
  1363.             return E_UNEXPECTED;
  1364.         }
  1365.         pWriterInputPin.Release();
  1366.  
  1367.         AM_MEDIA_TYPE *pmt2;
  1368.         hr = pStreamConfig->GetFormat( &pmt2 );
  1369.         if( SUCCEEDED( hr ) )
  1370.         {
  1371.             if(pmt2->majortype != MEDIATYPE_Video) {
  1372.                 DeleteMediaType(pmt2);
  1373.                 continue;
  1374.             }
  1375.             *ppmt = pmt2; // caller needs to delete
  1376.             return S_OK;
  1377.         }
  1378.     }
  1379.  
  1380.     return E_FAIL;
  1381. }
  1382.  
  1383.  
  1384. HRESULT MapProfileIdToProfile(
  1385.     int iProfile, IWMProfile **ppProfile)
  1386. {
  1387.     *ppProfile = 0;
  1388.     
  1389.     CComPtr <IWMProfileManager> pIWMProfileManager;
  1390.     HRESULT hr = WMCreateProfileManager( &pIWMProfileManager );
  1391.     if(FAILED(hr)) {
  1392.         return hr;
  1393.     }
  1394.  
  1395.     // we only use 7_0 profiles
  1396.     CComQIPtr<IWMProfileManager2, &IID_IWMProfileManager2> pIPM2(pIWMProfileManager);
  1397.     if(!pIWMProfileManager) {
  1398.         return E_UNEXPECTED;
  1399.     }
  1400.     hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 );
  1401.     if(FAILED(hr)) {
  1402.         return hr;
  1403.     }
  1404.  
  1405.     DWORD cProfiles;
  1406.     hr = pIWMProfileManager->GetSystemProfileCount(  &cProfiles );
  1407.     if(FAILED(hr)) {
  1408.         return hr;
  1409.     }
  1410.     if( (DWORD)iProfile >= cProfiles ) {
  1411.         printf("Invalid profile: %d\n", iProfile);
  1412.         return E_INVALIDARG;
  1413.     }
  1414.  
  1415.     return pIWMProfileManager->LoadSystemProfile( iProfile, ppProfile );
  1416. }
  1417.  
  1418. #endif // USE_WMF_CERT
  1419.